#region Using directives
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Text;
using Helper;
using SpaceWolf.Graphics;
using SpaceWolf.Shaders;
using SpaceWolf.Properties;
#endregion

namespace SpaceWolf
{
    /// <summary>
    /// Base game class for all the basic game support.
    /// Connects all our helper classes together and makes our live easier!
    /// </summary>
    public class BaseGame : Microsoft.Xna.Framework.Game
    {
        #region Variables

        static bool isFullScreen = GameSettings.Default.Fullscreen;

        public static bool IsFullScreen
        {
            get
            {
                return isFullScreen;
            }
        }

        /// <summary>
        /// Render delegate for rendering methods, also used for many other
        /// methods.
        /// </summary>
        public delegate void RenderDelegate();

        /// <summary>
        /// Epsilon (1/1000000) for comparing stuff which is nearly equal.
        /// </summary>
        public const float Epsilon = 0.000001f;
        /// <summary>
        /// For more accurate frames per second calculations,
        /// just count for one second, then fpsLastSecond is updated.
        /// Btw: Start with 1 to help some tests avoid the devide through zero
        /// problem.
        /// </summary>
        private static int
            frameCountThisSecond = 0,
            totalFrameCount = 0,
            fpsLastSecond = 60;
        /// <summary>
        /// Field of view and near and far plane distances for the
        /// ProjectionMatrix creation.
        /// </summary>
        private const float FieldOfView = (float)Math.PI / 2,
            NearPlane = 0.01f,//1.0f,
            FarPlane = 1000;//2000.0f;//1250.0f;//1750.0f;//2500.0f;//1000.0f;

        public const float ViewableFieldOfView = FieldOfView / 1.125f;

        private static float elapsedTimeThisFrameInMs = 0.001f, totalTimeMs = 0,
            lastFrameTotalTimeMs = 0;

        /// <summary>
        /// Helper for calculating frames per second. 
        /// </summary>
        private static float startTimeThisSecond = 0;
        /// <summary>
        /// Graphics
        /// </summary>
        protected GraphicsDeviceManager graphics;

        /// <summary>
        /// Content manager
        /// </summary>
        protected static ContentManager content = null;

        /// <summary>
        /// Device
        /// </summary>
        protected static GraphicsDevice device;

        /// <summary>
        /// Resolution of our game.
        /// </summary>
        protected static int width, height;

        

        /// <summary>
        /// Matrices for shaders. Used in a similar way than in Rocket Commander,
        /// but since we don't have a fixed function pipeline here we just use
        /// these values in the shader. Make sure to set all matrices before
        /// calling a shader. Inside a shader you have to update the shader
        /// parameter too, just setting the WorldMatrix alone does not work.
        /// </summary>
        private static Matrix worldMatrix,
            viewMatrix,
            projectionMatrix;

        /// <summary>
        /// Light direction, please read matrices info above for more details.
        /// The same things apply here.
        /// </summary>
        private static Vector3 lightDirection = new Vector3(0, 1, 0);

        /// <summary>
        /// Light direction
        /// </summary>
        /// <returns>Vector 3</returns>
        public static Vector3 LightDirection
        {
            get
            {
                return lightDirection;
            } // get
            set
            {
                lightDirection = value;
                lightDirection.Normalize();
            } // set
        } // LightDirection

        #region Camera
        /// <summary>
        /// World matrix
        /// </summary>
        /// <returns>Matrix</returns>
        public static Matrix WorldMatrix
        {
            get
            {
                return worldMatrix;
            } // get
            set
            {
                worldMatrix = value;
                // Update worldViewProj here?
            } // set
        } // WorldMatrix

        /// <summary>
        /// View matrix
        /// </summary>
        /// <returns>Matrix</returns>
        public static Matrix ViewMatrix
        {
            get
            {
                return viewMatrix;
            } // get
            set
            {
                // Set view matrix, usually only done in ChaseCamera.Update!
                viewMatrix = value;

                // Update camera pos and rotation, used all over the game!
                invViewMatrix = Matrix.Invert(viewMatrix);
                camPos = invViewMatrix.Translation;
                cameraRotation = Vector3.TransformNormal(
                    new Vector3(0, 0, 1), invViewMatrix);
            } // set
        } // ViewMatrix

        /// <summary>
        /// Projection matrix
        /// </summary>
        /// <returns>Matrix</returns>
        public static Matrix ProjectionMatrix
        {
            get
            {
                return projectionMatrix;
            } // get
            set
            {
                projectionMatrix = value;
                // Update worldViewProj here?
            } // set
        } // ProjectionMatrix

        /// <summary>
        /// Camera pos, updated each frame in ViewMatrix!
        /// Public to allow easy access from everywhere, will be called a lot each
        /// frame, for example Model.Render uses this for distance checks.
        /// </summary>
        private static Vector3 camPos;

        /// <summary>
        /// Get camera position from inverse view matrix. Similar to method
        /// used in shader. Works only if ViewMatrix is correctly set.
        /// </summary>
        /// <returns>Vector 3</returns>
        public static Vector3 CameraPos
        {
            get
            {
                return camPos;
            } // get
        } // CameraPos

        /// <summary>
        /// Camera rotation, used to compare objects for visibility.
        /// </summary>
        private static Vector3 cameraRotation = new Vector3(0, 1, 0);

        /// <summary>
        /// Aspect ratio of our current resolution
        /// </summary>
        private static float aspectRatio = 1.0f;

        /// <summary>
        /// Camera rotation
        /// </summary>
        /// <returns>Vector 3</returns>
        public static Vector3 CameraRotation
        {
            get
            {
                return cameraRotation;
            } // get
        } // CameraRotation

        /// <summary>
        /// Remember inverse view matrix.
        /// </summary>
        private static Matrix invViewMatrix;

        /// <summary>
        /// Inverse view matrix
        /// </summary>
        /// <returns>Matrix</returns>
        public static Matrix InverseViewMatrix
        {
            get
            {
                return invViewMatrix;//Matrix.Invert(ViewMatrix);
            } // get
        } // InverseViewMatrix

        /// <summary>
        /// View projection matrix
        /// </summary>
        /// <returns>Matrix</returns>
        public static Matrix ViewProjectionMatrix
        {
            get
            {
                return ViewMatrix * ProjectionMatrix;
            } // get
        } // ViewProjectionMatrix

        /// <summary>
        /// World view projection matrix
        /// </summary>
        /// <returns>Matrix</returns>
        public static Matrix WorldViewProjectionMatrix
        {
            get
            {
                return WorldMatrix * ViewMatrix * ProjectionMatrix;
            } // get
        } // WorldViewProjectionMatrix
        #endregion


        /// <summary>
        /// UI Renderer helper class for all 2d rendering.
        /// </summary>
        protected static UIRenderer ui = null;
        #endregion

        #region Properties

        /// <summary>
        /// Alpha blending
        /// </summary>
        /// <returns>Bool</returns>
        public static bool AlphaBlending
        {
            set
            {
                if (value)
                {
                    device.RenderState.AlphaBlendEnable = true;
                    device.RenderState.SourceBlend = Blend.SourceAlpha;
                    device.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
                } // if (value)
                else
                    device.RenderState.AlphaBlendEnable = false;
            } // set
        } // AlphaBlending
        /// <summary>
        /// Width
        /// </summary>
        /// <returns>Int</returns>
        public static int Width
        {
            get
            {
                return width;
            } // get
        } // Width

        /// <summary>
        /// Height
        /// </summary>
        /// <returns>Int</returns>
        public static int Height
        {
            get
            {
                return height;
            } // get
        } // Height

        /// <summary>
        /// Line manager 2D
        /// </summary>
        private static LineManager2D lineManager2D = null;
        /// <summary>
        /// Line manager 3D
        /// </summary>
        private static LineManager3D lineManager3D = null;

        /// <summary>
        /// Mesh render manager to render meshes of models in a highly
        /// optimized manner. We don't really have anything stored in here
        /// except for a sorted list on how to render everything based on the
        /// techniques and the materials and links to the renderable meshes.
        /// </summary>
        private static Graphics.MeshRenderManager meshRenderManager =
            new Graphics.MeshRenderManager();

        /// <summary>
        /// Mesh render manager to render meshes of models in a highly
        /// optimized manner.
        /// </summary>
        /// <returns>Mesh render manager</returns>
        public static Graphics.MeshRenderManager MeshRenderManager
        {
            get
            {
                return meshRenderManager;
            } // get
        } // MeshRenderManager

        /// <summary>
      
        public static GraphicsDevice Device
        {
            get
            {
                return device;
            } // get
        } // Device

        #region Content manager
        /// <summary>
        /// Content
        /// </summary>
        /// <returns>Content manager</returns>
        public static ContentManager ContentManager
        {
            get
            {
                return content;
            } // get
        } // Content
        #endregion

        #region Frames per second
        /// <summary>
        /// Fps
        /// </summary>
        /// <returns>Int</returns>
        public static int Fps
        {
            get
            {
                return fpsLastSecond;
            } // get
        } // Fps

        /*suxx, just use fpsInterpolated, it more accurate than overall checks
        /// <summary>
        /// Fps after 10 seconds
        /// </summary>
        /// <returns>Int</returns>
        public static int FpsAfter10Seconds
        {
            get
            {
                // Return 100 if 10 seconds have not passed yet.
                return TotalTime < 10 ? 100 :
                    // Else return total frames devided by the game time in sec.
                    (int)(totalFrameCount / TotalTime);
            } // get
        } // FpsAfter10Seconds
         */

        /// <summary>
        /// Interpolated fps over the last 10 seconds.
        /// Obviously goes down if our framerate is low.
        /// </summary>
        //private static float fpsInterpolated = 100.0f;

        /// <summary>
        /// Total frames
        /// </summary>
        /// <returns>Int</returns>
        public static int TotalFrames
        {
            get
            {
                return totalFrameCount;
            } // get
        } // TotalFrames
        #endregion

        #region Timing stuff
        /// <summary>
        /// Elapsed time this frame in ms
        /// </summary>
        /// <returns>Int</returns>
        public static float ElapsedTimeThisFrameInMilliseconds
        {
            get
            {
                return elapsedTimeThisFrameInMs;
            } // get
        } // ElapsedTimeThisFrameInMilliseconds

        /// <summary>
        /// Total time in seconds
        /// </summary>
        /// <returns>Int</returns>
        public static float TotalTime
        {
            get
            {
                return totalTimeMs / 1000.0f;
            } // get
        } // TotalTime

        /// <summary>
        /// Total time ms
        /// </summary>
        /// <returns>Float</returns>
        public static float TotalTimeMilliseconds
        {
            get
            {
                return totalTimeMs;
            } // get
        } // TotalTimeMilliseconds

        /// <summary>
        /// Move factor per second, when we got 1 fps, this will be 1.0f,
        /// when we got 100 fps, this will be 0.01f.
        /// </summary>
        public static float MoveFactorPerSecond
        {
            get
            {
                return elapsedTimeThisFrameInMs / 1000.0f;
            } // get
        } // MoveFactorPerSecond

        #endregion

        #region UI
        /// <summary>
        /// User interface renderer helper ^^
        /// </summary>
        /// <returns>UIRenderer</returns>
        public static UIRenderer UI
        {
            get
            {
                return ui;
            } // get
        } // UI
        #endregion

        #endregion

        #region Constructor
        /// <summary>
        /// Create base game
        /// </summary>
        public BaseGame(string sWindowTitle)
        {
            graphics = new GraphicsDeviceManager(this);
            
            content = Content;
            Content.RootDirectory = "Content";
            this.Window.Title = sWindowTitle;
            int resolutionWidth = GameSettings.Default.ResolutionWidth;
            int resolutionHeight = GameSettings.Default.ResolutionHeight;
            
            // Use current desktop resolution if autodetect is selected.
            if (resolutionWidth <= 0 ||
                resolutionHeight <= 0)
            {
                resolutionWidth =
                    GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
                resolutionHeight =
                    GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
            } // if (resolutionWidth)

            graphics.PreferredBackBufferWidth = resolutionWidth;
            graphics.PreferredBackBufferHeight = resolutionHeight;
            graphics.IsFullScreen = isFullScreen;

            this.IsFixedTimeStep = false;
        } // BaseGame()

        public BaseGame()
            : this("Game")
        {

        }
        /// <summary>
        /// Initialize
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();
            
            // Set device and resolution
            device = graphics.GraphicsDevice;
            width = device.Viewport.Width;//Window.ClientBounds.Width;
            height = device.Viewport.Height;//Window.ClientBounds.Height;
            //backBufferDepthFormat = graphicsManager.PreferredDepthStencilFormat;
           
            // Update resolution if it changes
            Window.ClientSizeChanged += new EventHandler(Window_ClientSizeChanged);
            graphics.DeviceReset += new EventHandler(graphics_DeviceReset);
            graphics_DeviceReset(null, EventArgs.Empty);
                       
            // Create matrices for our shaders, this makes it much easier
            // to manage all the required matrices since there is no fixed
            // function support and theirfore no Device.Transform class.
            WorldMatrix = Matrix.Identity;
            aspectRatio = (float)width / (float)height;
            ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(
                1, aspectRatio, NearPlane, FarPlane);

            // ViewMatrix is updated in camera class
            ViewMatrix = Matrix.CreateLookAt(new Vector3(0,0,150),Vector3.Zero, Vector3.Up);
            lineManager2D = new LineManager2D();
            lineManager3D = new LineManager3D();
            ui = new UIRenderer();
            
        } // Initialize()
        
        /// <summary>
        /// Graphics device reset
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">E</param>
        void graphics_DeviceReset(object sender, EventArgs e)
        {
            // Re-Set device
            device = graphics.GraphicsDevice;
            // Restore z buffer state
            BaseGame.Device.RenderState.DepthBufferEnable = true;
            BaseGame.Device.RenderState.DepthBufferWriteEnable = true;
            // Set u/v addressing back to wrap
            BaseGame.Device.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
            BaseGame.Device.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
            // Restore normal alpha blending
            //BaseGame.CurrentAlphaMode = BaseGame.AlphaMode.Default;

            // Set 128 and greate alpha compare for Model.Render
            BaseGame.Device.RenderState.ReferenceAlpha = 128;
            BaseGame.Device.RenderState.AlphaFunction = CompareFunction.Greater;

            //// Recreate all render-targets
            //foreach (RenderToTexture renderToTexture in remRenderToTextures)
            //    renderToTexture.HandleDeviceReset();
        } // graphics_DeviceReset(sender, e)

        /// <summary>
        /// Window client size changed
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">E</param>
        void Window_ClientSizeChanged(object sender, EventArgs e)
        {
            // Update width and height
            width = device.Viewport.Width;//Window.ClientBounds.Width;
            height = device.Viewport.Height;//Window.ClientBounds.Height;
            aspectRatio = (float)width / (float)height;
            ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(
                FieldOfView, aspectRatio, NearPlane, FarPlane);
        } // Window_ClientSizeChanged(sender, e)

        /// <summary>
        /// Load all graphics content (just our background texture).
        /// Use this method to make sure a device reset event is handled correctly.
        /// </summary>
        protected override void LoadContent()
        {
        } // LoadContent()

        /// <summary>
        /// Unload graphic content if the device gets lost.
        /// </summary>
        protected override void UnloadContent()
        {
            //SpriteHelper.Dispose();
            //BaseGame.MeshRenderManager.ClearAll();
            // Make sure there is nothing left
            content.Unload();
            Content.Unload();
            //Graphics.SpriteHelper.Dispose();
        } // UnloadContent()
        #endregion

        #region Update
        /// <summary>
        /// Update
        /// </summary>
        /// <param name="gameTime">Game time</param>
        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            lastFrameTotalTimeMs = totalTimeMs;
            elapsedTimeThisFrameInMs =
                (float)gameTime.ElapsedRealTime.TotalMilliseconds;
            totalTimeMs += elapsedTimeThisFrameInMs;

            // Make sure elapsedTimeThisFrameInMs is never 0
            if (elapsedTimeThisFrameInMs <= 0)
                elapsedTimeThisFrameInMs = 0.001f;
            
            // Increase frame counter for FramesPerSecond
            frameCountThisSecond++;
            totalFrameCount++;
            // One second elapsed?
			if (totalTimeMs - startTimeThisSecond > 1000.0f)
			{
				// Calc fps
				fpsLastSecond = (int)(frameCountThisSecond * 1000.0f /
					(totalTimeMs - startTimeThisSecond));
				// Reset startSecondTick and repaintCountSecond
				startTimeThisSecond = totalTimeMs;
				frameCountThisSecond = 0;

                //fpsInterpolated =
                //    MathHelper.Lerp(fpsInterpolated, fpsLastSecond, 0.1f);

				// Check out if our framerate is running very low. Then we can improve
				// rendering by reducing the number of objects we draw.
                //if (fpsInterpolated < 5)
                //    Model.MaxViewDistance = 50;
                //else if (fpsInterpolated < 12)
                //    Model.MaxViewDistance = 70;
                //else if (fpsInterpolated < 16)
                //    Model.MaxViewDistance = 90;
                //else if (fpsInterpolated < 20)
                //    Model.MaxViewDistance = 120;
                //else if (fpsInterpolated < 25)
                //    Model.MaxViewDistance = 150;
                //else if (fpsInterpolated < 30 ||
                //    HighDetail == false)
                //    Model.MaxViewDistance = 175;
			} // if (Math.Abs)
            
        } // Update(gameTime)
        #endregion

        /// <summary>
        /// Render delegate for rendering methods, also used for many other
        /// methods.
        /// </summary>
        public delegate void RenderHandler();

        #region Draw
        /// <summary>
        /// Flush line manager 2D. Renders all lines and allows more lines
        /// to be rendered. Used to render lines into textures and stuff.
        /// </summary>
        public static void FlushLineManager2D()
        {
            lineManager2D.Render();
        } // FlushLineManager2D()
        
        /// <summary>
        /// Flush line manager 3D. Renders all lines and allows more lines
        /// to be rendered.
        /// </summary>
        public static void FlushLineManager3D()
        {
            lineManager3D.Render();
        } // FlushLineManager3D()

        #region Line helper methods
        /// <summary>
        /// Draw line
        /// </summary>
        /// <param name="startPoint">Start point</param>
        /// <param name="endPoint">End point</param>
        /// <param name="color">Color</param>
        public static void DrawLine(Point startPoint, Point endPoint, Color color)
        {
            lineManager2D.AddLine(startPoint, endPoint, color);
        } // DrawLine(startPoint, endPoint, color)

        /// <summary>
        /// Draw line
        /// </summary>
        /// <param name="startPoint">Start point</param>
        /// <param name="endPoint">End point</param>
        public static void DrawLine(Point startPoint, Point endPoint)
        {
            lineManager2D.AddLine(startPoint, endPoint, Color.White);
        } // DrawLine(startPoint, endPoint)

        /// <summary>
        /// Draw line
        /// </summary>
        /// <param name="startPos">Start position</param>
        /// <param name="endPos">End position</param>
        /// <param name="color">Color</param>
        public static void DrawLine(Vector3 startPos, Vector3 endPos, Color color)
        {
            lineManager3D.AddLine(startPos, endPos, color);
        } // DrawLine(startPos, endPos, color)

        /// <summary>
        /// Draw line
        /// </summary>
        /// <param name="startPos">Start position</param>
        /// <param name="endPos">End position</param>
        /// <param name="startColor">Start color</param>
        /// <param name="endColor">End color</param>
        public static void DrawLine(Vector3 startPos, Vector3 endPos,
            Color startColor, Color endColor)
        {
            lineManager3D.AddLine(startPos, startColor, endPos, endColor);
        } // DrawLine(startPos, endPos, startColor)

        /// <summary>
        /// Draw line
        /// </summary>
        /// <param name="startPos">Start position</param>
        /// <param name="endPos">End position</param>
        public static void DrawLine(Vector3 startPos, Vector3 endPos)
        {
            lineManager3D.AddLine(startPos, endPos, Color.White);
        } // DrawLine(startPos, endPos)

#endregion

        #region Pixel helper methods
        /// <summary>
        /// Draw pixel
        /// </summary>
        /// <param name="startPoint">point</param>
        /// <param name="color">Color</param>
        public static void DrawPixel(Point point, Color color)
        {
            lineManager2D.AddPoint(point, color);
        } // DrawPixel(startPoint, color)

        /// <summary>
        /// Draws white pixel
        /// </summary>
        /// <param name="startPoint">point</param>
        /// <param name="color">Color</param>
        public static void DrawPixel(Point point)
        {
            lineManager2D.AddPoint(point, Color.White);
        } // DrawPixel(startPoint)
        #endregion

        public static void Draw(BoundingSphere sphere)
        {
            const int numCircleSegments = 12;

            float step = 2.0f * (float)Math.PI / numCircleSegments;

            for (int i = 0; i < numCircleSegments; ++i)
            {
                float u0 = (float)Math.Cos(step * i) * sphere.Radius;
                float v0 = (float)Math.Sin(step * i) * sphere.Radius;
                float u1 = (float)Math.Cos(step * (i + 1)) * sphere.Radius;
                float v1 = (float)Math.Sin(step * (i + 1)) * sphere.Radius;

                // xy
                BaseGame.DrawLine(new Vector3(u0, v0, 0) + sphere.Center, new Vector3(u1, v1, 0) + sphere.Center);

                // xz
                BaseGame.DrawLine(new Vector3(u0, 0, v0) + sphere.Center, new Vector3(u1, 0, v1) + sphere.Center);

                // yz
                BaseGame.DrawLine(new Vector3(0, u0, v0) + sphere.Center, new Vector3(0, u1, v1) + sphere.Center);
            }
        }


        /// <summary>
        /// Render
        /// </summary>
        protected virtual void Render()
        {
            // Overwrite this for your custom render code ..
        } // Render()

        /// <summary>
        /// Draw
        /// </summary>
        /// <param name="gameTime">Game time</param>
        protected override void Draw(GameTime gameTime)
        {
            ClearBackground();
            
            Render();
            
            //draw all meshes
            meshRenderManager.Render();

            // Render all 3d lines
            lineManager3D.Render();

            // Render UI and font texts, this also handles all collected
            // screen sprites (on top of 3d game code)
            UIRenderer.Render(lineManager2D);
            //if(SpaceWolf.GameLogic.GameManager.InMenu)
            ui.RenderTextsAndMouseCursor();
            base.Draw(gameTime);
        } // Draw(gameTime)
        
        /// <summary>
		/// Clear background
		/// </summary>
		public static void ClearBackground()
		{
			//unsure if it clears depth correctly: Device.Clear(BackgroundColor);
			Device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer,
				Color.Black, 1.0f, 0);
		} // ClearBackground()
		#endregion

        #region Helpers
        /// <summary>
        /// Calc rectangle, helper method to convert from our images (1024)
        /// to the current resolution. Everything will stay in the 16/9
        /// format of the textures.
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <param name="width">Width</param>
        /// <param name="height">Height</param>
        /// <returns>Rectangle</returns>
        public static Rectangle CalcRectangle(
            int relX, int relY, int relWidth, int relHeight)
        {
            float widthFactor = width / 1024.0f;
            float heightFactor = height / 640.0f;
            return new Rectangle(
                (int)Math.Round(relX * widthFactor),
                (int)Math.Round(relY * heightFactor),
                (int)Math.Round(relWidth * widthFactor),
                (int)Math.Round(relHeight * heightFactor));
        } // CalcRectangle(x, y, width)
        /// <summary>
        /// Calc rectangle, same method as CalcRectangle, but keep the 4 to 3
        /// ratio for the image. The Rect will take same screen space in
        /// 16:9 and 4:3 modes. E.g. Buttons should be displayed this way.
        /// Should be used for 1024px width graphics.
        /// </summary>
        /// <param name="gfxRect">Gfx rectangle</param>
        /// <returns>Rectangle</returns>
        public static Rectangle CalcRectangleKeep4To3(
            Rectangle gfxRect)
        {
            float widthFactor = width / 1024.0f;
            float heightFactor = height / 768.0f;
            return new Rectangle(
                (int)Math.Round(gfxRect.X * widthFactor),
                (int)Math.Round(gfxRect.Y * heightFactor),
                (int)Math.Round(gfxRect.Width * widthFactor),
                (int)Math.Round(gfxRect.Height * heightFactor));
        } // CalcRectangleKeep4To3(gfxRect)

        /// <summary>
        /// Calc rectangle, same method as CalcRectangle, but keep the 4 to 3
        /// ratio for the image. The Rect will take same screen space in
        /// 16:9 and 4:3 modes. E.g. Buttons should be displayed this way.
        /// Should be used for 1024px width graphics.
        /// </summary>
        /// <param name="relX">Rel x</param>
        /// <param name="relY">Rel y</param>
        /// <param name="relWidth">Rel width</param>
        /// <param name="relHeight">Rel height</param>
        /// <returns>Rectangle</returns>
        public static Rectangle CalcRectangleKeep4To3(
            int relX, int relY, int relWidth, int relHeight)
        {
            float widthFactor = width / 1024.0f;
            float heightFactor = height / 768.0f;
            return new Rectangle(
                (int)Math.Round(relX * widthFactor),
                (int)Math.Round(relY * heightFactor),
                (int)Math.Round(relWidth * widthFactor),
                (int)Math.Round(relHeight * heightFactor));
        } // CalcRectangleKeep4To3(relX, relY, relWidth)

        /// <summary>
        /// Calc rectangle for 1600px width graphics.
        /// </summary>
        /// <param name="relX">Rel x</param>
        /// <param name="relY">Rel y</param>
        /// <param name="relWidth">Rel width</param>
        /// <param name="relHeight">Rel height</param>
        /// <returns>Rectangle</returns>
        public static Rectangle CalcRectangle1600(
            int relX, int relY, int relWidth, int relHeight)
        {
            float widthFactor = width / 1600.0f;
            // keep height factor: float heightFactor = height / 1200.0f;
            float heightFactor = (height / 1200.0f);// / (aspectRatio / (16 / 9));
            return new Rectangle(
                (int)Math.Round(relX * widthFactor),
                (int)Math.Round(relY * heightFactor),
                (int)Math.Round(relWidth * widthFactor),
                (int)Math.Round(relHeight * heightFactor));
        } // CalcRectangle1600(relX, relY, relWidth)

        /// <summary>
        /// YTo res 768
        /// </summary>
        /// <param name="yIn768px">Y in 768px</param>
        /// <returns>Int</returns>
        public static int YToRes768(int yIn768px)
        {
            return (int)Math.Round(yIn768px * BaseGame.Height / 768.0f);
        } // YToRes768(yIn768px)

        /// <summary>
        /// XToRes helper method to convert 1024x640 to the current
        /// screen resolution. Used to position UI elements.
        /// </summary>
        /// <param name="xIn1024px">X in 1024px width resolution</param>
        /// <returns>Int</returns>
        public static int XToRes(int xIn1024px)
        {
            return (int)Math.Round(xIn1024px * BaseGame.Width / 1024.0f);
        } // XToRes(xIn1024px)

        /// <summary>
        /// Convert 3D vector to 2D vector, this is kinda the oposite of
        /// GetScreenPlaneVector (not shown here). This can be useful for user
        /// input/output, because we will often need the actual position on screen
        /// of an object in 3D space from the users view to handle it the right
        /// way. Used for lens flare and asteroid optimizations.
        /// </summary>
        /// <param name="point">3D world position</param>
        /// <return>Resulting 2D screen position</return>
        public static Point Convert3DPointTo2D(Vector3 point)
        {
            /*doesn't work, Vector3.Transform does not behave like
             * MDX Vector3.TransformCoordinate!
            Log.Write("point=" + point);
            Log.Write("ViewMatrix=" + ViewMatrix);
            Log.Write("ProjectionMatrix=" + ProjectionMatrix);
            Log.Write("ViewProjectionMatrix=" + ViewProjectionMatrix);
            Vector3 result = Vector3.Transform(point, ViewProjectionMatrix);
            Log.Write("result=" + result);
             */

            // Fix:
            Vector4 result4 = Vector4.Transform(point,
                ViewProjectionMatrix);

            //Log.Write("result4=" + result4);
            if (result4.W == 0)
                result4.W = BaseGame.Epsilon;
            Vector3 result = new Vector3(
                result4.X / result4.W,
                result4.Y / result4.W,
                result4.Z / result4.W);
            //Log.Write("result=" + result);

            // Output result from 3D to 2D
            return new Point(
                (int)Math.Round(+result.X * (width / 2)) + (width / 2),
                (int)Math.Round(-result.Y * (height / 2)) + (height / 2));
        } // Convert3DPointTo2D(point)

        /// <summary>
        /// Is point in front of camera?
        /// </summary>
        /// <param name="point">Position to check.</param>
        /// <returns>Bool</returns>
        public static bool IsInFrontOfCamera(Vector3 point)
        {
            Vector4 result = Vector4.Transform(
                new Vector4(point.X, point.Y, point.Z, 1),
                ViewProjectionMatrix);

            // Is result in front?
            return result.Z > result.W - NearPlane;
        } // IsInFrontOfCamera(point)

        /// <summary>
        /// Helper to check if a 3d-point is visible on the screen.
        /// Will basically do the same as IsInFrontOfCamera and Convert3DPointTo2D,
        /// but requires less code and is faster. Also returns just an bool.
        /// Will return true if point is visble on screen, false otherwise.
        /// Use the offset parameter to include points into the screen that are
        /// only a couple of pixel outside of it.
        /// </summary>
        /// <param name="point">Point</param>
        /// <param name="checkOffset">Check offset in percent of total
        /// screen</param>
        /// <returns>Bool</returns>
        public static bool IsVisible(Vector3 point, float checkOffset)
        {
            Vector4 result = Vector4.Transform(
                new Vector4(point.X, point.Y, point.Z, 1),
                ViewProjectionMatrix);

            // Point must be in front of camera, else just skip everything.
            if (result.Z > result.W - NearPlane)
            {
                Vector2 screenPoint = new Vector2(
                    result.X / result.W, result.Y / result.W);

                // Change checkOffset depending on how depth we are into the scene
                // for very near objects (z < 5) pass all tests!
                // for very far objects (z >> 5) only pass if near to +- 1.0f
                float zDist = Math.Abs(result.Z);
                if (zDist < 5.0f)
                    return true;
                checkOffset = 1.0f + (checkOffset / zDist);

                return
                    screenPoint.X >= -checkOffset && screenPoint.X <= +checkOffset &&
                    screenPoint.Y >= -checkOffset && screenPoint.Y <= +checkOffset;
            } // if (result.z)

            // Point is not in front of camera, return false.
            return false;
        } // IsVisible(point)
#endregion
    } // class BaseGame
} // namespace XnaTetris.Game